package island

import (
	"fmt"
	"github.com/opencontainers/runtime-spec/specs-go"
	"io"
)

var processInfo *ProcessInfo

func unregisterProcess(pid int) {
	processInfo.ProcessLock.Lock()
	delete(processInfo.ProcessMap, pid)
	processInfo.ProcessLock.Unlock()
}

func GetProcess(pid int) *Process {
	processInfo.ProcessLock.RLock()
	defer processInfo.ProcessLock.RUnlock()
	if res, ok := processInfo.ProcessMap[pid]; ok {
		return res
	} else {
		panic(pid)
	}
	return processInfo.ProcessMap[pid]
}

func NewProcess(options ProcessOptions) *Process {
	return &Process{
		ExitChan: make(chan struct{}),
		Process: specs.Process{
			Terminal: options.Terminal,
			User: specs.User{
				UID:      0,
				GID:      0,
				Username: "root",
			},
			Args:            options.Command,
			Env:             options.Env,
			Cwd:             options.WorkDir,
			NoNewPrivileges: true,
			Capabilities: &specs.LinuxCapabilities{
				Bounding: []string{
					"CAP_CHOWN",
					"CAP_DAC_OVERRIDE",
					"CAP_FSETID",
					"CAP_FOWNER",
					"CAP_MKNOD",
					"CAP_NET_RAW",
					"CAP_SETGID",
					"CAP_SETUID",
					"CAP_SETFCAP",
					"CAP_SETPCAP",
					"CAP_NET_BIND_SERVICE",
					"CAP_SYS_CHROOT",
					"CAP_KILL",
					"CAP_AUDIT_WRITE",
				},
				Permitted: []string{
					"CAP_CHOWN",
					"CAP_DAC_OVERRIDE",
					"CAP_FSETID",
					"CAP_FOWNER",
					"CAP_MKNOD",
					"CAP_NET_RAW",
					"CAP_SETGID",
					"CAP_SETUID",
					"CAP_SETFCAP",
					"CAP_SETPCAP",
					"CAP_NET_BIND_SERVICE",
					"CAP_SYS_CHROOT",
					"CAP_KILL",
					"CAP_AUDIT_WRITE",
				},
				Inheritable: []string{
					"CAP_CHOWN",
					"CAP_DAC_OVERRIDE",
					"CAP_FSETID",
					"CAP_FOWNER",
					"CAP_MKNOD",
					"CAP_NET_RAW",
					"CAP_SETGID",
					"CAP_SETUID",
					"CAP_SETFCAP",
					"CAP_SETPCAP",
					"CAP_NET_BIND_SERVICE",
					"CAP_SYS_CHROOT",
					"CAP_KILL",
					"CAP_AUDIT_WRITE",
				},
				Ambient: []string{
					"CAP_CHOWN",
					"CAP_DAC_OVERRIDE",
					"CAP_FSETID",
					"CAP_FOWNER",
					"CAP_MKNOD",
					"CAP_NET_RAW",
					"CAP_SETGID",
					"CAP_SETUID",
					"CAP_SETFCAP",
					"CAP_SETPCAP",
					"CAP_NET_BIND_SERVICE",
					"CAP_SYS_CHROOT",
					"CAP_KILL",
					"CAP_AUDIT_WRITE",
				},
				Effective: []string{
					"CAP_CHOWN",
					"CAP_DAC_OVERRIDE",
					"CAP_FSETID",
					"CAP_FOWNER",
					"CAP_MKNOD",
					"CAP_NET_RAW",
					"CAP_SETGID",
					"CAP_SETUID",
					"CAP_SETFCAP",
					"CAP_SETPCAP",
					"CAP_NET_BIND_SERVICE",
					"CAP_SYS_CHROOT",
					"CAP_KILL",
					"CAP_AUDIT_WRITE",
				},
			},
			Rlimits: []specs.POSIXRlimit{},
		},
	}
}

func (p *Process) SetConsoleSize(height, width uint) {
	p.ConsoleSize = &specs.Box{
		Height: height,
		Width:  width,
	}
}

func (p *Process) SetStdinWriter(writer io.Writer) {
	p.StdinWriter = writer
}

func (p *Process) SetStdoutReader(reader io.Reader) {
	p.StdoutReader = reader
}

func (p *Process) SetStderrReader(reader io.Reader) {
	p.StderrReader = reader
}

func (p *Process) SetStdioChan(channelList []chan struct{}) {
	p.StdinChan = channelList[0]
	p.StdoutChan = channelList[1]
	p.StderrChan = channelList[2]
}

func (p *Process) Wait() int {
	<-p.ExitChan
	unregisterProcess(p.Pid)
	return p.ExitCode
}

func (p *Process) WaitChan() chan struct{} {
	exitChan := make(chan struct{})
	go func() {
		p.Wait()
		close(exitChan)
	}()
	return exitChan
}

func (p *Process) SetExitCode(exit int) {
	p.ExitCode = exit
}

func (p *Process) StartProcess(start func() (int, error)) error {
	processInfo.ProcessLock.Lock()
	defer processInfo.ProcessLock.Unlock()

	pid, err := start()
	if err != nil {
		return err
	}
	p.Pid = pid
	processInfo.ProcessMap[pid] = p
	fmt.Println(p.Pid)
	return nil
}
